package bma.common.langutil.ai.stack;

import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import bma.common.langutil.ai.AIUtil;
import bma.common.langutil.ai.callback.AICallback;
import bma.common.langutil.ai.callback.AIResultObject;
import bma.common.langutil.ai.common.AIResultHandler;
import bma.common.langutil.ai.future.AIFutureCallbackable;
import bma.common.langutil.core.ExceptionUtil;
import bma.common.langutil.core.Function;

/**
 * 
 * @author guanzhong
 * @since 1.0 (imported from BMA.ESP Project)
 * 
 * @param <RESP_TYPE>
 */
public class AIStackSimple<RESP_TYPE> extends AIStackAbstract<RESP_TYPE>
		implements AIFutureCallbackable<RESP_TYPE> {

	private AIStack<?> parent;
	private AIResultObject<RESP_TYPE> data;
	private AICallback<RESP_TYPE> callback;

	public AIStackSimple(AIStack<?> p) {
		super();
		this.parent = p;
	}

	public boolean callback(AIResultHandler<RESP_TYPE> h) {
		if (parent == null)
			throw new NullPointerException("parent");
		return callback(AIUtil.callback(parent, h));
	}

	public boolean callback(AIStack<?> stack, AIResultHandler<RESP_TYPE> h) {
		return callback(AIUtil.callback(stack, h));
	}

	public <T_TYPE> boolean callback(AIStack<T_TYPE> stack,
			Function<RESP_TYPE, T_TYPE> transform) {
		return callback(AIUtil.callback(stack, transform));
	}

	@Override
	public AIStack<?> getParent() {
		return parent;
	}

	@Override
	public boolean callback(AICallback<RESP_TYPE> cb) {
		AIResultObject<RESP_TYPE> v = null;
		synchronized (this) {
			if (data != null) {
				v = data;
				data = null;
			} else {
				callback = cb;
				return false;
			}
		}
		end(v, cb);
		return true;
	}

	@Override
	public boolean success(RESP_TYPE result) {
		handle(new AIResultObject<RESP_TYPE>(result, null));
		return true;
	}

	@Override
	public boolean failure(Throwable t) {
		if (parent != null && callback == null) {
			return parent.failure(t);
		}
		handle(new AIResultObject<RESP_TYPE>(null, t));
		return true;
	}

	protected void handle(AIResultObject<RESP_TYPE> v) {
		AICallback<RESP_TYPE> cb = null;
		synchronized (this) {
			if (callback != null) {
				cb = callback;
				callback = null;
			} else {
				data = v;
				return;
			}
		}
		end(v, cb);
	}

	protected void end(AIResultObject<RESP_TYPE> v, AICallback<RESP_TYPE> cb) {
		if (v.isException()) {
			cb.onFailure(v.getThrowable());
		} else {
			try {
				cb.onSuccess(v.getResult());
			} catch (Exception err) {
				cb.onFailure(err);
			}
		}
	}

	public RESP_TYPE get(int timeout, TimeUnit unit)
			throws InterruptedException, TimeoutException, ExecutionException {
		return AIUtil.get(this, timeout, unit);
	}

	public RESP_TYPE get() throws InterruptedException, ExecutionException {
		return AIUtil.get(this);
	}

	public RESP_TYPE testcaseGet() {
		try {
			return get();
		} catch (Exception e) {
			throw ExceptionUtil.throwRuntime(e);
		}
	}

	public RESP_TYPE testcaseGet(int timeout, TimeUnit unit) {
		try {
			return get(timeout, unit);
		} catch (Exception e) {
			throw ExceptionUtil.throwRuntime(e);
		}
	}
}
